######################################################################
# Run using 'twistd -noy file.tac', then point your browser to
# http://localhost:8080
# A very simple Nevow site.
######################################################################

from twisted.application import service, internet
from twisted.python      import util
from zope.interface      import implements

from nevow               import rend
from nevow               import loaders
from nevow               import tags as T
from nevow               import inevow
from nevow               import flat
from nevow               import static
from nevow               import url

from formal              import iformal
import time, os, formal
import APN



# =========
# = Pages =
# =========
class MainPage ( rend.Page ):
    
    docFactory = loaders.xmlfile(util.sibpath(__file__, 'templates/MainPage.html'), ignoreDocType=True)
    addSlash = True
    
    def __init__(self,provider):
        super(MainPage, self).__init__()
        self.provider = provider
        
    def macro_page(self,ctx):
        """Look for self.__name__ Template and load it. Or present default if not available"""
        
        if self.__class__.__name__ == "MainPage":
            return None
        
        templatefile = util.sibpath(__file__,'templates/%s.html' % self.__class__.__name__)
        if os.path.exists(templatefile) and os.path.isfile(templatefile):
            return T.invisible[ T.p[
                loaders.xmlfile(templatefile,ignoreDocType=True).load()
                ]]
            # return T.invisible[T.p["Sowas aber auch"]]
        
        return None
        

    def child_ListDevices(self, ctx):
        """docstring for child_listDevices"""
        return ListDevices(self.provider)
        
    def child_Status(self,ctx):
        """docstring for child_Status"""
        return Status(self.provider)
    
    def child_SendMultiAlert(self,ctx):
        """docstring for child_SendNotification"""
        return SendMultiAlert(self.provider)
    
    def child_Device(self,ctx):
        """docstring for child_Device"""
        return Device(self.provider)
        
    def child_css(self, ctx):
        return static.File(util.sibpath(__file__, 'templates/styles.css'))
        


class NotificationForm(formal.ResourceMixin, MainPage):
    
    options_hasActionKey = [(True,"Default \"View\""),('Custom','Custom:')]
    options_alertTypes = [('Forced','Force! (Piss off your users!)')]
    
    def __init__(self, *args, **kwargs):
        super(NotificationForm, self).__init__(*args, **kwargs)
        self.provider = args[0]
        self.kwargs = kwargs
        self.formDefaults = {
            'Alert.alertType': u'Default',
            'Alert.LocKey': False,
            'Alert.isLocalizable': False,
            'Alert.incrementBadge': True,
            'Alert.playSound':True,
            'Alert.hasActionKey':False
        }
        
    def makeDeviceToken(self,hidden=True):
        if hidden:
            field = formal.Field('deviceToken', formal.String(strip=True,required=True,validators=[DeviceTokenValidator()]),widgetFactory=formal.Hidden)
        else:
            field = formal.Field('deviceToken', formal.String(strip=True,required=True,validators=[DeviceTokenValidator()]))
        field.label = "Devicetoken"
        field.description = Help("deviceToken")
        field.cssClass = "formblock-deviceToken"
        return field
    
    def makeAlert(self):
        fields = []
        
        alert = formal.Group("Alert")
        alert.description = Help("alert")
        
        alertBody = formal.Field('alertBody', formal.String(strip=True,required=True))
        alertBody.label = 'Message'
        alertBody.description = Help("alertBody")
        
        alertLocalizable = formal.Field('locKey', formal.Boolean())
        alertLocalizable.label = 'Localizable? (Loc-Key)'
        alertLocalizable.description = Help("locKey")
        
        alertKeys = formal.Field('LocArgs',formal.String(strip=True))
        alertKeys.label = 'Loc-Args'
        alertKeys.description = Help('alertKeys')
        
        alertIncrementBadge = formal.Field('incrementBadge', formal.Boolean())
        alertIncrementBadge.label = "Increment Badge"
        alertIncrementBadge.description = Help("incrementBadge")
        
        alertPlaySound = formal.Field('playSound', formal.Boolean())
        alertPlaySound.label = "Play Sound"
        alertPlaySound.description = Help("playSound")
        
        alertHasActionKey = formal.Field('hasActionKey', formal.Boolean())
        alertHasActionKey.label = "Action Key"
        alertHasActionKey.description = Help("hasActionKey")
        
        alertCustomActionKey = formal.Field('customActionKey', formal.String())
        alertCustomActionKey.label = "Action Key Label"
        alertCustomActionKey.description = Help("customActionKey")
        
        
        
        alertType = formal.Field('alertType',formal.String(strip=True),formal.widgetFactory(formal.SelectChoice, options=self.options_alertTypes,noneOption=("default","Default")))
        alertType.label = "Alert Type"
        alertType.description = Help("alertType")
        alertType.cssClass = "formblock-alertType"
        
        alert.add(alertType)
        alert.add(alertBody)
        alert.add(alertIncrementBadge)
        alert.add(alertKeys)
        alert.add(alertLocalizable)
        alert.add(alertCustomActionKey)
        alert.add(alertHasActionKey)
        alert.add(alertPlaySound)
        
        return alert
    

class SendSingleAlert(NotificationForm):
    
    def form_sendSingleAlert(self, ctx):
        form = formal.Form()
        
        form.add(self.makeDeviceToken())
        form.add(self.makeAlert())
        
        form.data = self.formDefaults
        form.data['deviceToken'] = ctx.arg("devicetoken")
        
        form.addAction(self.sendSingleAlert)
        return form
    
    def sendSingleAlert(self, ctx, form, data):
        print "Form is: %s Data is: %s" % (form, data)
        try:
            device = self.provider.getDeviceWithDevicetoken(data['deviceToken'])
            
            if data['Alert.customActionKey'] is not None and len(data['Alert.customActionKey']) > 0:
                 data['Alert.hasActionKey'] = data['Alert.customActionKey']
            
            #sendAlert(self,body,isLocalizable=False,incrementBadge=True,playSound=True,hasActionKey=False,*locargs,**custom)            
            device.sendAlert(data['Alert.alertBody'],
                                            isLocalizable=data['Alert.isLocalizable'],
                                            incrementBadge=data['Alert.incrementBadge'],
                                            playSound=data['Alert.playSound'],
                                            hasActionKey=data['Alert.hasActionKey'],
                                            alertType=data['Alert.alertType']
                                            )
        except APN.provider.NoSuchDevice:
            return "No such Device"
            
        return "Alright"
        
    

class SendMultiAlert(NotificationForm):
    
    def form_sendMultiAlert(self, ctx):
        form = formal.Form()
        
        form.add(self.makeAlert())
        
        form.data = self.formDefaults
        
        form.addAction(self.sendMultiAlert)
        return form
    
    
    def sendMultiAlert(self, ctx, form, data):
        
        if data['customActionKey'] is not None and len(data['customActionKey']) > 0:
             data['hasActionKey'] = data['customActionKey']
             
        devices = self.provider.getDevices()
        
        for device in devices:
        
            #sendAlert(self,body,isLocalizable=False,incrementBadge=True,playSound=True,hasActionKey=False,*locargs,**custom)            
            device.sendAlert(data['alert'],
                                            isLocalizable=data['isLocalizable'],
                                            incrementBadge=data['incrementBadge'],
                                            playSound=data['playSound'],
                                            hasActionKey=data['hasActionKey'],
                                            alertType=data['alertType']
                                            )
        
        return "Alright"
    

class SendSingleBadge(NotificationForm):
    """docstring for sendSingleBadge"""
    def __init__(self, arg):
        super(sendSingleBadge, self).__init__()
        self.arg = arg
        
    # =====================
    # = Send Single Badge =
    # =====================
    def form_sendSingleBadge(self, ctx):
        form = formal.Form()
        
        form.addField('deviceToken', formal.String(strip=True,required=True,validators=[DeviceTokenValidator()]))        
        form.addField('setBadgeTo', formal.Integer())
        
        try:
            device = self.provider.getDeviceWithDevicetoken(ctx.arg('devicetoken'))
        except:
            databaseBadgeCount = None
            pass
        else:
            databaseBadgeCount = device.getBadgeCount()
        
        form.data = {
            'deviceToken':ctx.arg('devicetoken'),
            'setBadgeTo':databaseBadgeCount
            }
        
        
        form.addAction(self.sendSingleBadge)
        return form
    
    
    def sendSingleBadge(self, ctx, form, data):
        try:
            device = self.provider.getDeviceWithDevicetoken(data['deviceToken'])
            
            device.badgeSetTo(data['setBadgeTo'])
            
        except APN.provider.NoSuchDevice:
            return "No such Device"
            
        return "Alright"
        
    

class Status(MainPage):
    
    def Something(self):
        """docstring for Something"""
        pass


class Device(SendSingleAlert):

    def render_DeviceInfo(self,ctx,data):
        """docstring for render_deviceInfo"""
        ctx.fillSlots('registered','May 2009')
        ctx.fillSlots('updated','August 2009')
        return ctx.tag

    def form_editDeviceSettings(self,ctx):
        """docstring for lala"""
        form = formal.Form()
        return form


class ListDevices (MainPage):
    
    
    def data_devices(self,ctx,data):
        """Returns a list of Device Dictionaries. These are read from Provider Object."""
        for device in self.provider.getDevices():
            yield dict (
                            deviceToken=device.getDeviceToken(),
                            badgeCount=device.getBadgeCount(),
                            badgeSetting=device.getBadgeSetting(),
                            soundSetting=device.getSoundSetting(),
                            alertSetting=device.getAlertSetting(),
                            lastModified=device.getLastModified(),
                            failCount=device.getFailCount()
                        )
                        
    
    def render_deviceRow(self, ctx, data):
        ctx.fillSlots('send', T.a(id=data["deviceToken"], href="/Device/?devicetoken=%s" % data["deviceToken"])["Send Notification"])
        ctx.fillSlots('deviceToken', T.a(id=data["deviceToken"], href="/Device/?devicetoken=%s" % data["deviceToken"])[data["deviceToken"]])
        ctx.fillSlots('badgeCount',data["badgeCount"])
        ctx.fillSlots('badgeSetting',data["badgeSetting"])
        ctx.fillSlots('soundSetting',data["soundSetting"])
        ctx.fillSlots('alertSetting',data["alertSetting"])
        ctx.fillSlots('lastModified',data["lastModified"])
        ctx.fillSlots('failCount',data["failCount"])
        return ctx.tag
        



# ==============
# = Validation =
# ==============

class DeviceTokenValidator(object):
    implements(iformal.IValidator)
    
    def validate(self,field,deviceToken):
        """Checks if the deviceToken is in the correct format. 32byte long, Hex encoded"""
        #29d445f72b4b26431efb90c673f751af64ed1745fdba4b832e1f244651aacca0
        try:
            try:
                bintoken = deviceToken.decode("hex")
            except TypeError:
                raise formal.FieldValidationError(u'Not Hex')
            if len(bintoken) is not 32:
                raise formal.FieldValidationError(u'Devicetokens must be 32bytes (64 chars) long. The provided one is %s bytes long' % len(bintoken))
        except:
            raise formal.FieldValidationError(u'Invalid')


    


# ========
# = Help =
# ========
class Help(object):
    """Help System."""
    def __init__(self, name):
        """Looks in help directory for a file named 'name'.html. This files is expected to be a nevow template."""
        super(Help, self).__init__()
        self.name = name
        templatefile = util.sibpath(__file__,'help/%s.html' % self.name)
        if os.path.exists(templatefile) and os.path.isfile(templatefile):
            self.help = loaders.xmlfile(templatefile,ignoreDocType=True).load()
        else:
            self.help = loaders.stan(T.p["No help available on this item. This is considered a bug."])
    
    def flatten(self):
        return self.help
    



def flattenHelp(original, context):
    return original.flatten()

flat.registerFlattener(flattenHelp, Help)

